package com.xinqi.xinqidev.ws;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.xinqi.xinqidev.bigmodel.entity.JsonParse;
import com.xinqi.xinqidev.bigmodel.entity.Text;
import com.xinqi.xinqidev.bigmodel.gptutils.AuthUrlUtils;
import com.xinqi.xinqidev.bigmodel.gptutils.HistoryUtils;
import com.xinqi.xinqidev.bigmodel.gptutils.WebSocketUtils;
import com.xinqi.xinqidev.bigmodel.secret.PersonalGPTSecret;
import com.xinqi.xinqidev.config.GetHttpSessionConfig;
import com.xinqi.xinqidev.bigmodel.entity.RoleContent;
import com.xinqi.xinqidev.config.ServerEncoder;
import com.xinqi.xinqidev.pojo.entity.QuestionAnswerLog;
import com.xinqi.xinqidev.pojo.result.Result;
import com.xinqi.xinqidev.pojo.result.ResultUtil;
import com.xinqi.xinqidev.service.QuestionAnswerLogService;
import com.xinqi.xinqidev.utils.JsonUtils;
import com.xinqi.xinqidev.utils.MessageUtils;
import com.xinqi.xinqidev.ws.pojo.Message;
import com.xinqi.xinqidev.ws.pojo.ResultMessage;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.java_websocket.server.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import static com.xinqi.xinqidev.common.BigModelContents.HOST_URL;

/**
 * 监听websocket连接
 */
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfig.class,encoders = { ServerEncoder.class })
@Component
@Slf4j
public class ChatEndpoint {

    private HttpSession httpSession;

    private String user;//用户名称

    private Session session;//session对象，用来发送远程的请求

    private WebSocket webSocket;

    private BigModelNew gptModel;

    private String mess;

    public List<RoleContent> historyList = new ArrayList<>(); // 对话历史存储集合

    // 个性化参数
    private Long userId;


    private static QuestionAnswerLogService questionAnswerLogService;

    @Autowired
    public void setActionLogMapper(QuestionAnswerLogService questionAnswerLogService) {
        ChatEndpoint.questionAnswerLogService = questionAnswerLogService;
    }

    @Getter
    class BigModelNew extends WebSocketListener {
        // 环境治理的重要性  环保  人口老龄化  我爱我的祖国
        private String NewQuestion = "";
        private Boolean totalFlag = true; // 控制提示用户是否输入
        public String totalAnswer = ""; // 大模型的答案汇总
        // 构造函数
        public BigModelNew(String NewQuestion) {
            this.NewQuestion = NewQuestion;
        }

        @Override
        public void onOpen(WebSocket webSocket, Response response) {
            super.onOpen(webSocket, response);
            System.out.print("大模型：");
            sendMessage(webSocket);
        }

        private void sendMessage(WebSocket webSocket) {
            try {
                JSONObject requestJson = new JSONObject();

                JSONObject header = new JSONObject();  // header参数-header部分
                header.put("app_id", PersonalGPTSecret.getAppId());
                header.put("uid", UUID.randomUUID().toString().substring(0, 10));

                JSONObject parameter = new JSONObject(); // parameter参数-parameter.chat部分
                JSONObject chat = new JSONObject();
                chat.put("domain", "generalv2");
                chat.put("temperature", 0.5);
                chat.put("max_tokens", 2048);//https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
//                chat.put("max_tokens", 4096);
                parameter.put("chat", chat);

                JSONObject payload = new JSONObject(); // payload参数
                JSONObject message = new JSONObject();
                JSONArray text = new JSONArray();

                // 历史问题获取
                if (historyList.size() > 0) {
                    for (RoleContent tempRoleContent : historyList) {
                        text.add(JSON.toJSON(tempRoleContent));
                    }
                }
                // 最新问题
                RoleContent roleContent = new RoleContent();
                roleContent.setRole("user");
                roleContent.setContent(NewQuestion);
                text.add(JSON.toJSON(roleContent));
                historyList.add(roleContent);
                message.put("text", text);
                payload.put("message", message);

                requestJson.put("header", header);
                requestJson.put("parameter", parameter);
                requestJson.put("payload", payload);
                // System.err.println(requestJson); // 可以打印看每次的传参明细
                webSocket.send(requestJson.toString());

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * text 是1.4 接口响应里的payload.
         *
         * @param webSocket
         * @param text
         */
        @Override
        public void onMessage(WebSocket webSocket, String text) {
            // System.out.println(userId + "用来区分那个用户的结果" + text);
            JsonParse myJsonParse = JsonUtils.gson.fromJson(text, JsonParse.class);
            if (myJsonParse.getHeader().getCode() != 0) {
                System.out.println("发生错误，错误码为：" + myJsonParse.getHeader().getCode());
                String sId = myJsonParse.getHeader().getSid();
                System.out.println("本次请求的sid为：" + sId);
                log.debug("webSocket3 is " + webSocket);


                questionAnswerLogService.save(//这里无法使用mybatis
                        new QuestionAnswerLog()
                                .setUserId(userId)
                                .setQuestion(NewQuestion)
                                .setAnswer(totalAnswer)
                                .setIsExistException(true)
                                .setSId(sId)
                );

                //help gc
                WebSocketUtils.closeAndGC(webSocket);
                totalFlag = true;

            }

            List<Text> textList = myJsonParse.getPayload().getChoices().getText();
            for (Text temp : textList) {
//            System.out.println("answer is "+ temp.getContent());//这个应该是是输出
                System.out.print(temp.getContent());//这里是输出 todo temp.getContent() 需要插入数据库，字段：
                // todo answer for 用户id(bigint) ,createTime(初始时创建即可) , answer(估计是一个blog),question(估计也是blog)
                //todo 万一 answer缺失怎么办？会不会出现这个情况？那么数据库的表的字段需要增加什么字段吗？
                // 思考：缺失就缺失吧，只能再加一个字段来标志是否缺失，所以说这个字段应该是tinyInt 要么0要么1，默认为1代表问题回答完毕。反之表示回答存在异常。
                totalAnswer += temp.getContent();
            }

            if (myJsonParse.getHeader().getStatus() == 2) {

                ResultMessage response = MessageUtils.getMessageObject(totalAnswer);//todo 返回的内容
                log.debug("要保存到数据库的totalAnswer是：" + totalAnswer);
                QuestionAnswerLog questionAnswerLog = new QuestionAnswerLog()
                        .setUserId(userId)
                        .setQuestion(NewQuestion)
                        .setAnswer(totalAnswer);

                log.debug("questionAnswerLog is " + questionAnswerLog);
                boolean isSuccess = questionAnswerLogService.save(
                        questionAnswerLog
                );//这里保存不了，为什么？应该是没有给予spring来管理
                log.debug("isSuccess is " + isSuccess);

                log.debug("数据即将发送。。。。。。。");
                //totalAnswer为最后的结果。
                try {
                    session.getBasicRemote().sendObject(ResultUtil.ok(response));//发送给客户端。
                } catch (Exception e) {
                    e.printStackTrace();
                    log.debug("数据: " + response + " \n 发送失败。。。。");
                }

                // 可以关闭连接，释放资源

                RoleContent roleContent = new RoleContent("assistant", totalAnswer);

                //如果size不足以12000，那么可以继续走：
                if (!HistoryUtils.canAddHistory(historyList)) historyList.remove(0);

                historyList.add(roleContent);//这里是添加gpt的回复。

                //这里是等到执行完毕，就关掉这个webSocket
                WebSocketUtils.closeAndGC(webSocket);
                //这个是最后一个，什么是最后一个？就是gpt回复你的问题可能分多次返回，于是因此多次返回前，输入的问题是不会被被gpt回答的。
                // 这里可以做一个接收中途的问题到一个队列里面，当之前的回答都打完了就从该队列里获取问题继续相应回答。
                //不过这里也可以不去做一个队列，直接就是阻塞。通过totalFlag做阻塞。
                totalFlag = true;
            }
        }
        @Override
        public void onFailure(WebSocket webSocket, Throwable t, Response response) {
            super.onFailure(webSocket, t, response);
            try {
                if (null != response) {
                    int code = response.code();
                    System.out.println("onFailure code:" + code);
                    System.out.println("onFailure body:" + response.body().string());
                    if (101 != code) {
                        System.out.println("connection failed");
                        System.exit(0);
                    }
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }


    /**
     * 建立websocket连接后，被调用
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //1，将session进行保存
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        user = (String) this.httpSession.getAttribute("user");
        userId = Long.valueOf((String) this.httpSession.getAttribute("userId"));
        log.debug("user is " + user);
        this.session = session;
    }

    /**
     * 浏览器发送消息到服务端，该方法被调用
     * 张三  -->  李四
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {

        if (gptModel != null && !gptModel.getTotalFlag()) {

            log.debug("gpt还未返回全部数据，不可以输入");
            WebSocketUtils.closeAndGC(webSocket);

            return;
        }


        try {
            //将消息推送给指定的用户
            Message msg = JSON.parseObject(message, Message.class);
            log.debug("msg is " + msg);
            //获取 消息接收方的用户名
            String toName = msg.getToName();
            mess = msg.getMessage();

            if (!StringUtils.hasText(mess)) {
                //数据发送
                session.getBasicRemote().sendObject(ResultUtil.fail("输入信息不能为空"));//发送给客户端。
                log.debug("空串 is " + mess);

                return;
            }

            //获取消息接收方用户对象的session对象
            getAnswerFromGpt(mess);

        } catch (Exception e) {
            //记录日志
            log.debug("e is " + e);
        }
    }

    //todo
    private void getAnswerFromGpt(String mess) throws Exception {
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 构建鉴权url
        String authUrl = AuthUrlUtils.getAuthUrl(
                HOST_URL,
                PersonalGPTSecret.getApiKey(),
                PersonalGPTSecret.getApiSecret());

        String url = authUrl.toString().
                replace("http://", "ws://").
                replace("https://", "wss://");

        Request request = new Request.Builder().url(url).build();
        gptModel = new BigModelNew(mess);

        webSocket = client.newWebSocket(request, gptModel);
    }


    /**
     * 断开 websocket 连接时被调用
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        //要不要做点什么？
        System.out.println("关闭了。。。。" + session);
    }

    /**
     * 配置错误信息处理
     *
     * @param session
     * @param t
     */
    @OnError
    public void onError(Session session, Throwable t) {
        //什么都不想打印都去掉就好了
        log.info("【websocket消息】出现未知错误 ");
        //打印错误信息，如果你不想打印错误信息，去掉就好了
        //这里打印的也是  java.io.EOFException: null
        t.printStackTrace();
    }

}
